import java.util.*;

/**
 * @author LKQ
 * @date 2022/5/26 17:49
 * @description 线段树 采用动态开点：动态指针方式实现，有效避免数组估点问题，有效避免 new 大数组的初始化开销。
 */
public class SegmentTree2 {



    static class Node {
        /**
         * left, right表示当前区间的左右子节点
         */
        Node left, right;
        /**
         * val: sum(l, r), max: max(l, r), min: min(l, r), add: 懒加载标记
         */
        long val, max, min, add;
    }


    public static int N = 10 ;
    public static Node root = new Node();
    public static int[] data = new int[N];

    public void pushUp(Node p) {
        // 向上不断维护区间
        p.val = p.left.val + p.right.val;
    }

    public void pushUpMax(Node p) {
        p.max = Math.max(p.left.max, p.right.max);
    }

    public void pushUpMin(Node p) {
        p.min = Math.min(p.left.min, p.right.min);
    }

    public void build(Node p, int l , int r) {
        // 递归建树
        if (p.left == null) {
            p.left = new Node();
        }
        if (p.right == null) {
            p.right = new Node();
        }
        if (l == r) {
            // 如果左右区间相同，那么必然是叶子节点，只有叶子节点是被真实赋值的
            p.val = data[l];
            return;
        }
        int mid = l + ((r - l) >> 1);
        build(p.left, l , mid);
        build(p.right, mid + 1, r);
        // 通过子节点来维护父节点
        pushUp(p);
        pushUpMax(p);
        pushUpMin(p);
    }
    /**
     * 修改区间 [s, t]内每个数 + k。
     * @param p 当前节点 p
     * @param l p存储的区间 [l, r]
     * @param r p存储的区间 [l, r]
     */
    public void update(Node p, int s, int t, int l, int r, int val) {
        if (l <= s && t <= r) {
            // 当前区间[s, t]是修改区间[l, r]的子集时，直接修改当前节点的值，然后打标记，结束修改
            p.val += (long) (r - l + 1) * val;
            p.add += val;
            return;
        }
        // 回溯之前，更新子节点值和懒加载标记
        pushDown(p, s, t);
        int mid = s + ((t - s) >> 1);

        if (l <= mid) {
            update(p.left, s, mid, l, r, val);
        }
        if (r > mid) {
            update(p.right, mid + 1, t, l, r, val);
        }
        pushUp(p);
        pushUpMin(p);
        pushUpMax(p);
    }

    public void lazy(Node p, int l, int r, int k) {
        // 懒加载函数，向下传递
        if (k == 0) {
            return;
        }
        // 打上懒加载标记
        p.add += k;
        // 区间和
        p.val += (long) k * (r - l + 1);
    }
    public void pushDown(Node p, int l, int r) {
        // 懒加载向下传递 [l, r]为当前节点p的存储区间
        if (p.left == null) {
            p.left = new Node();
        }
        if (p.right == null) {
            p.right = new Node();
        }
        int mid = l + ((r - l) >> 1);
        // 懒加载向下传递
        lazy(p.left, l, mid, (int) p.add);
        lazy(p.right, mid + 1, r, (int) p.add);
        // 传递完毕后置0
        p.add = 0;
    }
    /**
     * 查询区间 [x, y]的区间和、最大值，最小值
     * @param l p节点存储的区间 [l, r]
     * @param r p节点存储的区间 [l, r]
     * @param p p节点
     * @return 查询值
     */
    public long query(Node p, int l, int r, int x, int y) {
        long sum = 0;
        if (x <= l && r <= y) {
            return p.val;
        }
        int mid = l + ((r - l) >> 1);
        pushDown(p, l , r);
        if (x <= mid) {
            sum += query(p.left, l, mid, x, y);
        }
        if (y > mid) {
            sum += query(p.right, mid + 1, r, x, y);
        }
        return sum;
    }

    public int queryMax(Node p, int l, int r, int x, int y) {
        int max = Integer.MIN_VALUE;
        if (x <= l && r <= y) {
            return (int) p.max;
        }
        int mid = l + ((r - l) >> 1);
        pushDown(p, l, r);
        if (x <= mid) {
            max = Math.max(max, queryMax(p.left, l, mid, x, y));
        }
        if (y > mid) {
            max = Math.max(max, queryMax(p.right, mid + 1, r, x , y));
        }
        return max;
    }
    public int queryMin(Node p, int l, int r, int x, int y) {
        int min = Integer.MAX_VALUE;
        if (x <= l && r <= y) {
            return (int) p.min;
        }
        int mid = l + ((r - l) >> 1);
        pushDown(p, l, r);
        if (x <= mid) {
            min = Math.min(min, queryMin(p.left, l, mid, x, y));
        }
        if (y > mid) {
            min = Math.min(min, queryMin(p.right, mid + 1, r, x , y));
        }
        return min;
    }
    public static void main(String[] args) {
        SegmentTree2 segmentTree2 = new SegmentTree2();

        // 第一行包含两个整数 n, m，分别表示该数列数字的个数和操作的总个数
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(), m = sc.nextInt();
        sc.nextLine();
        // 第二行包含n个由空格分割的整数，其中第i个数字表示数列第i项的初始值
        for (int i = 1; i <= n; i++) {
            data[i] = sc.nextInt();
        }
        sc.nextLine();

        segmentTree2.build(root, 1, N);
        // 接下来m行，每行包含3/4个整数，表示一种操作
        // 1 x y k, 表示 将区间[x, y]内每个数加k,
        // 2 x y 表示输出【x, y】内每个数的和
        // 3 x y 表示输出【x, y】之间的最大值
        // 4 x y 表示输出【x, y】之间的最小值
        while (m-- > 0) {
            int op = sc.nextInt();
            switch (op) {
                case 1:
                    int x = sc.nextInt(), y = sc.nextInt(), k = sc.nextInt();
                    segmentTree2.update(root, 1, N, x , y, k);
                    break;
                case 2:
                    int a = sc.nextInt(), b = sc.nextInt();
                    System.out.println(segmentTree2.query(root, 1, N, a, b));
                    break;
                case 3:
                    int c = sc.nextInt(), d = sc.nextInt();
                    System.out.println(segmentTree2.queryMax(root, 1, N, c, d));
                    break;
                case 4:
                    int e = sc.nextInt(), f = sc.nextInt();
                    System.out.println(segmentTree2.queryMin(root, 1, N, e, f));
                    break;
                default:
                    break;
            }
            sc.nextLine();
        }
    }
}
